import sys
import asyncio
from datetime import datetime, timedelta
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                           QHBoxLayout, QLabel, QPushButton, QDateTimeEdit,
                           QFileDialog, QLineEdit, QMessageBox, QSpinBox,
                           QComboBox, QGroupBox, QDialog, QTextEdit, QDialogButtonBox,
                           QTabWidget)
from PyQt6.QtCore import Qt, QTimer, QDateTime, QTime
from PyQt6.QtGui import QClipboard, QTextCursor
from websocket.ws_handler import WebSocketManager
from websocket.ws_routes import ws_manager
from websocket.mock_data import MockDataGenerator
import threading
import uvicorn
from main import app
from utils.db_manager import db_manager
from utils.logger import logger
import socket
from config.settings import WS_PORT, PORT_CONFIG
from utils.time_utils import TimeRangeHandler
from utils.data_fetcher import KlineDataFetcher
from utils.price_processor import PriceProcessor, PriceProcessStrategy
from utils.data_cache import data_cache
from config.kline_settings import KLINE_GRANULARITY, MAX_RANGE_MINUTES
import tkinter as tk
from tkinter import ttk, messagebox
from utils.network_utils import is_port_in_use, find_available_port
from service.price_matcher import PriceMatcher
import logging
import sqlite3

class CustomDataDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.init_ui()
        
    def init_ui(self):
        self.setWindowTitle("自定义数据")
        self.setMinimumSize(400, 300)
        layout = QVBoxLayout()
        info_label = QLabel("请输入自定义价格数据，每行一个数字：")
        layout.addWidget(info_label)
        self.data_text = QTextEdit()
        self.data_text.setPlaceholderText("示例：\n1800.0\n1805.0\n1810.0\n1808.0")
        layout.addWidget(self.data_text)
        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        layout.addWidget(button_box)
        self.setLayout(layout)
    
    def get_custom_data(self):
        text = self.data_text.toPlainText().strip()
        if not text:
            return []
        try:
            data = []
            for line in text.split('\n'):
                line = line.strip()
                if line:
                    value = float(line)
                    data.append(value)
            return data
        except ValueError:
            return []

def is_port_in_use(port: int, host: str = '0.0.0.0') -> bool:
    """检查端口是否被占用"""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        try:
            s.bind((host, port))
            return False
        except socket.error:
            return True

def find_available_port(start_port: int = WS_PORT, host: str = '0.0.0.0') -> int:
    """查找可用端口"""
    port = start_port
    while is_port_in_use(port, host):
        port += 1
    return port

class DataViewerDialog(QDialog):
    """数据查看对话框"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.init_ui()
        
    def init_ui(self):
        self.setWindowTitle("数据查看器")
        self.setMinimumSize(600, 400)
        
        layout = QVBoxLayout()
        
        # 数据信息区域
        info_group = QGroupBox("数据信息")
        info_layout = QVBoxLayout()
        self.info_label = QLabel()
        info_layout.addWidget(self.info_label)
        info_group.setLayout(info_layout)
        layout.addWidget(info_group)
        
        # 数据显示区域
        data_group = QGroupBox("价格数据")
        data_layout = QVBoxLayout()
        self.data_text = QTextEdit()
        self.data_text.setReadOnly(True)
        data_layout.addWidget(self.data_text)
        data_group.setLayout(data_layout)
        layout.addWidget(data_group)
        
        # 按钮区域
        button_box = QDialogButtonBox()
        self.copy_button = QPushButton("复制数据")
        self.copy_button.clicked.connect(self.copy_data)
        button_box.addButton(self.copy_button, QDialogButtonBox.ButtonRole.ActionRole)
        button_box.addButton(QDialogButtonBox.StandardButton.Close)
        button_box.rejected.connect(self.reject)
        layout.addWidget(button_box)
        
        self.setLayout(layout)
        self.update_display()
        
    def update_display(self):
        """更新显示内容"""
        # 更新信息显示
        info = data_cache.get_data_info()
        info_text = (
            f"数据总数: {info['total_count']}\n"
            f"时间范围: {info['time_range']}\n"
            f"K线粒度: {info['granularity']}\n"
            f"处理策略: {info['strategy']}"
        )
        self.info_label.setText(info_text)
        
        # 更新数据显示
        prices = data_cache.get_data()
        if prices:
            self.data_text.setText("\n".join(map(str, prices)))
        else:
            self.data_text.setText("暂无数据")
            
    def copy_data(self):
        """复制数据到剪贴板"""
        clipboard = QApplication.clipboard()
        clipboard.setText(self.data_text.toPlainText())
        QMessageBox.information(self, "提示", "数据已复制到剪贴板")

class LogMonitorDialog(QDialog):
    """撮合轮询日志监控对话框"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("撮合轮询日志监控")
        self.setMinimumSize(1000, 700)
        layout = QVBoxLayout()
        # 轮询日志区
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        layout.addWidget(QLabel("轮询日志："))
        layout.addWidget(self.log_text)
        # 订单消息区
        self.order_text = QTextEdit()
        self.order_text.setReadOnly(True)
        layout.addWidget(QLabel("订单消息："))
        layout.addWidget(self.order_text)
        # 订单缓存区（左右两栏）
        layout.addWidget(QLabel("订单缓存："))
        from PyQt6.QtWidgets import QHBoxLayout, QVBoxLayout as QVBoxLayout2
        order_cache_layout = QHBoxLayout()
        # 开多订单
        open_layout = QVBoxLayout2()
        open_label = QLabel("开多订单")
        self.open_orders_text = QTextEdit()
        self.open_orders_text.setReadOnly(True)
        open_layout.addWidget(open_label)
        open_layout.addWidget(self.open_orders_text)
        # 平多订单
        close_layout = QVBoxLayout2()
        close_label = QLabel("平多订单")
        self.close_orders_text = QTextEdit()
        self.close_orders_text.setReadOnly(True)
        close_layout.addWidget(close_label)
        close_layout.addWidget(self.close_orders_text)
        order_cache_layout.addLayout(open_layout)
        order_cache_layout.addLayout(close_layout)
        layout.addLayout(order_cache_layout)
        # 刷新控制区
        refresh_layout = QHBoxLayout()
        self.manual_refresh_btn = QPushButton("手动刷新订单缓存")
        self.auto_refresh_btn = QPushButton("开启自动刷新")
        self.stop_auto_refresh_btn = QPushButton("关闭自动刷新")
        self.stop_auto_refresh_btn.setEnabled(False)
        refresh_layout.addWidget(self.manual_refresh_btn)
        refresh_layout.addWidget(self.auto_refresh_btn)
        refresh_layout.addWidget(self.stop_auto_refresh_btn)
        layout.addLayout(refresh_layout)
        self.setLayout(layout)
        # 日志缓冲区
        self.log_buffer = []
        self.order_buffer = []
        # 订单缓存刷新定时器
        self.order_timer = QTimer(self)
        self.order_timer.timeout.connect(self.update_order_cache)
        # 定时刷新日志
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_log)
        self.timer.start(1000)
        # 注册自定义日志Handler
        self.handler = GuiLogHandler(self)
        logging.getLogger().addHandler(self.handler)
        # 绑定按钮
        self.manual_refresh_btn.clicked.connect(self.manual_refresh_cache)
        self.auto_refresh_btn.clicked.connect(self.start_auto_refresh)
        self.stop_auto_refresh_btn.clicked.connect(self.stop_auto_refresh)
        # 初始显示订单缓存
        self.update_order_cache()
        # 默认开启自动刷新 - 添加这行代码
        self.start_auto_refresh()  # 这将自动调用start_auto_refresh方法

    def update_log(self):
        self.log_text.setPlainText("\n".join(self.log_buffer[-100:]))
        self.log_text.moveCursor(QTextCursor.MoveOperation.End)
        # 订单消息栏高亮已成交订单
        html_lines = []
        for msg in self.order_buffer[-100:]:
            if '[撮合成交]' in msg:
                html_lines.append(f"<span style='color:green;'>{msg}</span>")
            else:
                html_lines.append(msg)
        self.order_text.setHtml("<br>".join(html_lines))
        self.order_text.moveCursor(QTextCursor.MoveOperation.End)

    def append_log(self, msg):
        self.log_buffer.append(msg)
        if len(self.log_buffer) > 1000:
            self.log_buffer = self.log_buffer[-1000:]

    def append_order(self, msg):
        self.order_buffer.append(msg)
        if len(self.order_buffer) > 1000:
            self.order_buffer = self.order_buffer[-1000:]
            
    def log_message(self, msg):
        """记录日志消息到日志缓冲区，并通过logger输出"""
        self.append_log(msg)
        logger.info(msg)

    def update_order_cache(self):
        """从数据库刷新订单缓存"""
        try:
            # 刷新订单缓存
            from cache.order_cache import order_cache
            order_cache.refresh()
            
            # 获取开仓和平仓订单
            open_orders = order_cache.get_open_orders()
            close_orders = order_cache.get_close_orders()
            
            # 记录订单数量
            #logger.info(f"[GUI] 刷新订单缓存完成，检索到 {len(open_orders)} 个开仓订单和 {len(close_orders)} 个平仓订单")
            
            # 更新UI
            self.update_order_list_ui(open_orders, close_orders)
            
            return True
        except Exception as e:
            logger.error(f"[GUI] 刷新订单缓存失败: {str(e)}")
            return False

    def manual_refresh_cache(self):
        """手动刷新订单缓存"""
        self.log_message("[GUI] 手动刷新订单缓存...")
        
        # 调用更新函数并获取结果
        success = self.update_order_cache()
        
        if success:
            self.log_message("[GUI] 手动刷新订单缓存成功")
            QMessageBox.information(self, "刷新成功", "订单数据已成功刷新")
        else:
            self.log_message("[GUI] 手动刷新订单缓存失败")
            QMessageBox.warning(self, "刷新失败", "订单数据刷新失败，请查看日志获取详细信息")

    def start_auto_refresh(self):
        """启动自动刷新功能"""
        # 如果定时器已经在运行，先停止它
        if hasattr(self, 'auto_refresh_timer') and self.auto_refresh_timer.isActive():
            self.auto_refresh_timer.stop()
            
        self.log_message("[GUI] 启动自动刷新功能...")
        
        # 创建并启动定时器，每5秒刷新一次
        self.auto_refresh_timer = QTimer()
        self.auto_refresh_timer.timeout.connect(self.auto_refresh_cache)
        self.auto_refresh_timer.start(5000)  # 5秒
        
        # 更新UI状态
        self.auto_refresh_btn.setText("停止自动刷新")
        self.auto_refresh_btn.setStyleSheet("background-color: #d9534f;")
        self.stop_auto_refresh_btn.setEnabled(True)
        self.log_message("[GUI] 自动刷新已启动，间隔: 5秒")

    def stop_auto_refresh(self):
        """停止自动刷新功能"""
        if hasattr(self, 'auto_refresh_timer'):
            self.auto_refresh_timer.stop()
            
        # 更新UI状态
        self.auto_refresh_btn.setText("开启自动刷新")
        self.auto_refresh_btn.setStyleSheet("")
        self.stop_auto_refresh_btn.setEnabled(False)
        self.log_message("[GUI] 自动刷新已停止")
        
    def auto_refresh_cache(self):
        """自动刷新订单缓存的处理函数"""
        success = self.update_order_cache()
        # 自动刷新失败时，不显示弹窗，只记录日志
    def closeEvent(self, event):
        logging.getLogger().removeHandler(self.handler)
        self.order_timer.stop()
        event.accept()

    def update_order_list_ui(self, open_orders, close_orders):
        """更新订单列表UI显示"""
        try:
            # 格式化开仓订单显示
            open_lines = [f"订单ID:{o['order_id']} | 价格:{o['open_price']} | 模式:{o['mode']}" for o in open_orders]
            self.open_orders_text.setPlainText("\n".join(open_lines) if open_lines else "无开仓订单")
            
            # 格式化平仓订单显示
            close_lines = [f"订单ID:{o['order_id']} | 价格:{o['close_price']} | 模式:{o['mode']}" for o in close_orders]
            self.close_orders_text.setPlainText("\n".join(close_lines) if close_lines else "无平仓订单")
            
            # 确保UI更新
            self.open_orders_text.update()
            self.close_orders_text.update()
        except Exception as e:
            logger.error(f"[GUI] 更新订单列表UI失败: {str(e)}")
            QMessageBox.warning(self, "更新失败", f"更新订单列表UI失败: {str(e)}")

class GuiLogHandler(logging.Handler):
    def __init__(self, dialog):
        super().__init__()
        self.dialog = dialog
    def emit(self, record):
        msg = self.format(record)
        if '[撮合轮询]' in msg:
            self.dialog.append_log(msg)
        if '[撮合成交]' in msg:
            self.dialog.append_order(msg)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ws_manager = ws_manager
        self.mock_generator = MockDataGenerator()
        self.data_fetcher = None
        self.server_running = False
        self.server_thread = None
        self.current_port = None
        self.price_matcher = None
        self.log_monitor_dialog = None
        self.ws_client_update_timer = QTimer()
        self.ws_client_update_timer.timeout.connect(self.update_ws_client_count)
        self.ws_client_update_timer.start(1000)
        self.initUI()
        
    def initUI(self):
        self.setWindowTitle("量化发射器-交易回溯")
        self.setMinimumSize(800, 600)
        
        # 主窗口部件
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        self.main_layout = QVBoxLayout(main_widget)
        
        # 初始化UI组件
        self.init_db_section()
        self.init_connection_section()
        self.init_kline_section()
        self.init_control_section()
        self.init_status_section()
        
        # 更新状态的定时器
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_status)
        self.timer.start(1000)  # 每秒更新一次
        
    def init_db_section(self):
        """初始化数据库设置部分"""
        db_group = QGroupBox("客户端数据库")
        db_layout = QVBoxLayout()
        
        # 数据库文件选择
        file_layout = QHBoxLayout()
        db_label = QLabel("数据库文件:")
        self.db_path_edit = QLineEdit()
        self.db_path_edit.setReadOnly(True)
        self.db_path_edit.setText(db_manager.DB_PATH)
        select_db_btn = QPushButton("选择数据库")
        select_db_btn.clicked.connect(self.select_db_file)
        test_db_btn = QPushButton("测试")
        test_db_btn.clicked.connect(self.test_db_connection)
        file_layout.addWidget(db_label)
        file_layout.addWidget(self.db_path_edit)
        file_layout.addWidget(select_db_btn)
        file_layout.addWidget(test_db_btn)
        
        db_layout.addLayout(file_layout)
        db_group.setLayout(db_layout)
        self.main_layout.addWidget(db_group)

    def init_connection_section(self):
        """初始化连接服务设置部分"""
        connection_group = QGroupBox("连接服务设置")
        connection_layout = QVBoxLayout()
        
        # 端口设置
        port_layout = QHBoxLayout()
        port_label = QLabel("服务端口:")
        self.port_spinbox = QSpinBox()
        self.port_spinbox.setRange(PORT_CONFIG["MIN_PORT"], PORT_CONFIG["MAX_PORT"])
        self.port_spinbox.setValue(PORT_CONFIG["DEFAULT_WS_PORT"])
        self.port_spinbox.valueChanged.connect(self.check_port)
        self.port_status = QLabel("端口状态: 未检查")
        
        # WebSocket服务控制按钮
        self.start_ws_btn = QPushButton("启动服务")
        self.stop_ws_btn = QPushButton("停止服务")
        self.stop_ws_btn.setEnabled(False)
        self.start_ws_btn.clicked.connect(self.start_websocket_server)
        self.stop_ws_btn.clicked.connect(self.stop_websocket_server)
        
        port_layout.addWidget(port_label)
        port_layout.addWidget(self.port_spinbox)
        port_layout.addWidget(self.port_status)
        port_layout.addWidget(self.start_ws_btn)
        port_layout.addWidget(self.stop_ws_btn)
        
        connection_layout.addLayout(port_layout)
        connection_group.setLayout(connection_layout)
        self.main_layout.addWidget(connection_group)
        
        # 初始检查端口状态
        self.check_port()

    def init_kline_section(self):
        """初始化K线设置部分"""
        kline_group = QGroupBox("历史数据配置")
        kline_layout = QVBoxLayout()

        # 创建两列布局
        grid_layout = QHBoxLayout()
        left_column = QVBoxLayout()
        right_column = QVBoxLayout()

        # === 左列 ===
        # 时间范围选择
        time_range_group = QGroupBox("数据时间范围")
        time_range_layout = QVBoxLayout()
        
        # 开始时间
        start_layout = QHBoxLayout()
        start_label = QLabel("开始时间:")
        self.start_time = QDateTimeEdit()
        # 设置为整点时间
        current_time = QDateTime.currentDateTime()
        start_time = current_time.addDays(-1)
        start_time.setTime(QTime(start_time.time().hour(), 0))
        self.start_time.setDateTime(start_time)
        self.start_time.setDisplayFormat("yyyy-MM-dd HH:mm")
        self.start_time.setCalendarPopup(True)  # 允许弹出日历
        self.start_time.setTimeSpec(Qt.TimeSpec.LocalTime)  # 使用本地时间
        start_layout.addWidget(start_label)
        start_layout.addWidget(self.start_time)
        
        # 结束时间
        end_layout = QHBoxLayout()
        end_label = QLabel("结束时间:")
        self.end_time = QDateTimeEdit()
        end_time = current_time
        end_time.setTime(QTime(end_time.time().hour(), 0))
        self.end_time.setDateTime(end_time)
        self.end_time.setDisplayFormat("yyyy-MM-dd HH:mm")
        self.end_time.setCalendarPopup(True)  # 允许弹出日历
        self.end_time.setTimeSpec(Qt.TimeSpec.LocalTime)  # 使用本地时间
        end_layout.addWidget(end_label)
        end_layout.addWidget(self.end_time)

        # 时间跨度显示
        self.time_span_label = QLabel()
        self.time_span_label.setStyleSheet("color: red;")
        self.update_time_span()  # 初始更新时间跨度

        # 连接时间变化信号
        self.start_time.dateTimeChanged.connect(self.update_time_span)
        self.end_time.dateTimeChanged.connect(self.update_time_span)
        
        time_range_layout.addLayout(start_layout)
        time_range_layout.addLayout(end_layout)
        time_range_layout.addWidget(self.time_span_label)
        time_range_group.setLayout(time_range_layout)
        left_column.addWidget(time_range_group)

        # === 右列 ===
        # K线粒度选择
        granularity_group = QGroupBox("K线粒度")
        granularity_layout = QHBoxLayout()
        self.granularity_combo = QComboBox()
        for code, name, _ in KLINE_GRANULARITY:
            self.granularity_combo.addItem(f"{name} ({code})", code)
        granularity_layout.addWidget(self.granularity_combo)
        granularity_group.setLayout(granularity_layout)
        right_column.addWidget(granularity_group)

        # 扫描频率设置
        scan_group = QGroupBox("扫描频率")
        scan_layout = QHBoxLayout()
        self.scan_seconds = QSpinBox()
        self.scan_seconds.setRange(1, 3600)
        self.scan_seconds.setValue(1)  # 默认为1秒
        scan_seconds_label = QLabel("秒")
        self.scan_times = QSpinBox()
        self.scan_times.setRange(1, 1000)
        self.scan_times.setValue(1)
        scan_times_label = QLabel("次")
        
        scan_layout.addWidget(self.scan_seconds)
        scan_layout.addWidget(scan_seconds_label)
        scan_layout.addWidget(self.scan_times)
        scan_layout.addWidget(scan_times_label)
        scan_group.setLayout(scan_layout)
        right_column.addWidget(scan_group)

        # 添加两列到网格布局
        grid_layout.addLayout(left_column)
        grid_layout.addLayout(right_column)
        kline_layout.addLayout(grid_layout)

        # === 底部操作区域 ===
        bottom_group = QGroupBox("数据操作")
        bottom_layout = QHBoxLayout()
        
        # 获取历史数据按钮
        self.fetch_history_button = QPushButton("设定交易回溯数据")
        self.fetch_history_button.clicked.connect(self.fetch_history_data)
        
        # 查看数据按钮
        self.view_data_button = QPushButton("查看历史数据")
        self.view_data_button.clicked.connect(self.show_data_viewer)
        
        # 自定义数据按钮
        self.custom_data_button = QPushButton("自定义数据")
        self.custom_data_button.clicked.connect(self.show_custom_data_dialog)
        
        # K线图按钮
        self.kline_chart_button = QPushButton("显示K线图")
        self.kline_chart_button.clicked.connect(self.show_kline_chart)
        
        # K线数据状态
        self.kline_status_label = QLabel("K线数据: 未获取")
        
        # 匹配状态
        self.matching_status_label = QLabel("撮合轮询: 未启动")
        
        bottom_layout.addWidget(self.fetch_history_button)
        bottom_layout.addWidget(self.view_data_button)
        bottom_layout.addWidget(self.custom_data_button)
        bottom_layout.addWidget(self.kline_chart_button)
        bottom_layout.addWidget(self.kline_status_label)
        bottom_layout.addWidget(self.matching_status_label)
        bottom_layout.addStretch()
        bottom_group.setLayout(bottom_layout)
        kline_layout.addWidget(bottom_group)

        kline_group.setLayout(kline_layout)
        self.main_layout.addWidget(kline_group)

    def init_control_section(self):
        """初始化控制按钮部分"""
        control_group = QGroupBox("控制台")
        control_layout = QHBoxLayout()  # 改为水平布局
        
        # 创建左侧按钮容器
        left_buttons_layout = QHBoxLayout()
        
        # 设置圆形按钮的样式
        circle_button_style = """
            QPushButton {
                border-radius: 40px;
                min-width: 80px;
                min-height: 80px;
                max-width: 80px;
                max-height: 80px;
                padding: 10px;
                font-size: 14px;
                font-weight: bold;
                border: 2px solid #cccccc;
            }
            QPushButton:hover {
                border: 2px solid #666666;
            }
            QPushButton:pressed {
                border: 2px solid #333333;
            }
            QPushButton:disabled {
                background-color: #f8f8f8;
                border: 2px solid #e0e0e0;
                color: #999999;
            }
        """
        
        # 开始和停止模拟按钮
        self.start_button = QPushButton("开始\n模拟")
        self.stop_button = QPushButton("停止\n模拟")
        
        # 设置按钮样式
        self.start_button.setStyleSheet(circle_button_style + """
            QPushButton {
                background-color: #90EE90;
                color: #006400;
            }
            QPushButton:hover {
                background-color: #98FB98;
            }
            QPushButton:pressed {
                background-color: #32CD32;
            }
        """)
        
        self.stop_button.setStyleSheet(circle_button_style + """
            QPushButton {
                background-color: #FFB6C1;
                color: #8B0000;
            }
            QPushButton:hover {
                background-color: #FFC0CB;
            }
            QPushButton:pressed {
                background-color: #FF69B4;
            }
        """)
        
        self.stop_button.setEnabled(False)
        
        # 添加按钮到左侧布局
        left_buttons_layout.addWidget(self.start_button)
        left_buttons_layout.addSpacing(20)  # 添加间距
        left_buttons_layout.addWidget(self.stop_button)
        left_buttons_layout.addStretch()  # 添加弹性空间
        
        # 监控按钮样式
        monitor_button_style = """
            QPushButton {
                padding: 8px 15px;
                background-color: #4a90e2;
                color: white;
                border-radius: 4px;
                border: none;
                font-size: 12px;
                min-width: 100px;
                min-height: 40px;
            }
            QPushButton:hover {
                background-color: #357abd;
            }
            QPushButton:pressed {
                background-color: #2a5f9e;
            }
        """
        
        # 监控按钮
        self.monitor_button = QPushButton("监控")
        self.monitor_button.setStyleSheet(monitor_button_style)
        
        # 将左侧按钮布局和监控按钮添加到主布局
        control_layout.addLayout(left_buttons_layout, stretch=4)  # 左侧占据更多空间
        control_layout.addWidget(self.monitor_button, alignment=Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)  # 右对齐并垂直居中
        
        control_group.setLayout(control_layout)
        self.main_layout.addWidget(control_group)
        
        # 连接信号
        self.start_button.clicked.connect(self.start_simulation)
        self.stop_button.clicked.connect(self.stop_simulation)
        self.monitor_button.clicked.connect(self.show_log_monitor)
        self.granularity_combo.currentIndexChanged.connect(self.on_granularity_changed)

    def init_status_section(self):
        """初始化状态显示部分"""
        status_group = QGroupBox("状态")
        main_layout = QHBoxLayout()  # 使用水平布局
        
        # 左侧状态信息
        left_layout = QVBoxLayout()
        
        # 数据库状态
        self.db_status_label = QLabel("数据库状态: 未连接")
        left_layout.addWidget(self.db_status_label)
        
        # K线数据状态
        self.data_status_label = QLabel("K线数据: 未获取")
        left_layout.addWidget(self.data_status_label)
        
        # 服务器状态
        self.server_status_label = QLabel("服务器状态: 未启动")
        left_layout.addWidget(self.server_status_label)
        
        # WebSocket客户端数量
        self.ws_client_count_label = QLabel("WebSocket连接数: 0")
        left_layout.addWidget(self.ws_client_count_label)
        
        # 右侧使用说明
        right_layout = QVBoxLayout()
        instruction_label = QLabel("模拟发射器的使用方式:")
        instruction_label.setStyleSheet("font-weight: bold; color: #333333;")
        right_layout.addWidget(instruction_label)
        
        instructions = [
            "第一步：需要先选择需要模拟客户端的数据库文件，一般为sqlite数据库文件",
            "第二步：在连接服务设置里面，点击启动服务",
            "第三步：在历史数据配置栏目里面，设置好回溯的数据范围，可以选择1分钟粒度或者其他的，默认是1分钟粒度的数据",
            "第四步：点击设定交易回溯数据，可以点击查看历史数据，浏览历史数据",
            "第五步：点击开始模拟，整个流程就开始跑起来了。可以在监控里面，查看模拟的全过程"
        ]
        
        for i, text in enumerate(instructions, 1):
            step_label = QLabel(text)
            step_label.setWordWrap(True)  # 允许文字换行
            step_label.setStyleSheet("color: #666666; margin: 2px;")
            right_layout.addWidget(step_label)
        
        right_layout.addStretch()  # 添加弹性空间
        
        # 将左右布局添加到主布局
        main_layout.addLayout(left_layout, 1)  # 左侧状态信息占比较小
        main_layout.addLayout(right_layout, 2)  # 右侧说明占比较大
        
        status_group.setLayout(main_layout)
        self.main_layout.addWidget(status_group)

    def check_port(self):
        """检查端口是否可用"""
        port = self.port_spinbox.value()
        if is_port_in_use(port):
            self.port_status.setText("端口状态: 已被占用")
            self.port_status.setStyleSheet("color: red")
            return False
        else:
            self.port_status.setText("端口状态: 可用")
            self.port_status.setStyleSheet("color: green")
            return True

    def select_db_file(self):
        """选择数据库文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "选择数据库文件",
            "",
            "SQLite数据库文件 (*.db);;所有文件 (*.*)"
        )
        if file_path:
            self.db_path_edit.setText(file_path)
            db_manager.update_db_path(file_path)
            self.update_db_status()

    def update_db_status(self):
        """更新数据库状态显示和按钮状态"""
        if db_manager.is_connected:
            self.db_status_label.setText("数据库状态: 已连接")
            if hasattr(self, 'connect_db_btn'): # Ensure buttons exist
                self.connect_db_btn.setEnabled(False)
                self.disconnect_db_btn.setEnabled(True)
        else:
            self.db_status_label.setText("数据库状态: 未连接")
            if hasattr(self, 'connect_db_btn'):
                self.connect_db_btn.setEnabled(True)
                self.disconnect_db_btn.setEnabled(False)

    def on_granularity_changed(self):
        """当K线粒度改变时的处理"""
        pass  # 移除自动校验

    def validate_time_range(self) -> bool:
        """验证时间范围是否有效"""
        start_dt = self.start_time.dateTime().toPyDateTime()
        end_dt = self.end_time.dateTime().toPyDateTime()
        
        if start_dt >= end_dt:
            QMessageBox.warning(self, "警告", "开始时间必须早于结束时间！")
            return False

        granularity = self.granularity_combo.currentData()
        max_minutes = MAX_RANGE_MINUTES[granularity]
        time_ranges = TimeRangeHandler.split_time_range(start_dt, end_dt, granularity)
        
        if len(time_ranges) > 500:  # 设置一个合理的最大请求次数
            QMessageBox.warning(
                self,
                "警告",
                f"选择的时间范围过大！\n"
                f"当前粒度({granularity})下，每个请求最多返回{max_minutes}分钟的数据。\n"
                f"建议缩小时间范围或选择更大的粒度。"
            )
            return False
        
        return True

    def show_data_viewer(self):
        """显示数据查看器"""
        dialog = DataViewerDialog(self)
        dialog.exec()

    def show_log_monitor(self):
        if self.log_monitor_dialog is None or not self.log_monitor_dialog.isVisible():
            self.log_monitor_dialog = LogMonitorDialog(self)
            self.log_monitor_dialog.show()
        else:
            self.log_monitor_dialog.raise_()
            self.log_monitor_dialog.activateWindow()

    async def fetch_kline_data(self):
        """获取K线数据"""
        start_dt = self.start_time.dateTime().toPyDateTime()
        end_dt = self.end_time.dateTime().toPyDateTime()
        granularity = self.granularity_combo.currentData()
        
        self.kline_status_label.setText("K线数据: 正在获取...")
        try:
            # 存储每个时间段的数据
            time_series_data = []
            
            # 用于存储完整OHLC数据
            all_raw_data = []
            
            # 获取所有时间范围
            time_ranges = TimeRangeHandler.split_time_range(start_dt, end_dt, granularity)
            
            for start, end in time_ranges:
                # 获取该时间段的数据
                data = await self.data_fetcher.fetch_kline_data(
                    "ETHUSDT",  # 这里可以添加交易对选择
                    granularity,
                    start,
                    end
                )
                
                if data:
                    # 保存原始OHLC数据
                    all_raw_data.extend(data)
                    
                    # 处理该时间段的数据
                    processed_prices = PriceProcessor.process_kline_data(
                        data,
                        PriceProcessStrategy.HIGH_LOW_ONLY  # 使用最高价和最低价策略
                    )
                    # 添加到时间序列数据中
                    time_series_data.append((start, processed_prices))
            
            # 存储完整OHLC数据到管理器
            from utils.ohlc_data_manager import ohlc_data_manager
            ohlc_count = ohlc_data_manager.store_ohlc_data(all_raw_data, granularity, start_dt, end_dt)
            logger.info(f"已保存 {ohlc_count} 条完整OHLC数据用于图表显示")
            
            if time_series_data:
                # 合并所有时间段的数据
                all_prices = PriceProcessor.merge_time_series_data(time_series_data)
                self.kline_status_label.setText(f"K线数据: 已获取并处理 {len(all_prices)} 条")
                
                # 更新数据缓存
                data_cache.update_data(
                    all_prices,
                    start_dt,
                    end_dt,
                    granularity,
                    "最高价和最低价"
                )
                
                return all_prices
            else:
                self.kline_status_label.setText("K线数据: 获取失败")
                return []
                
        except Exception as e:
            error_msg = f"K线数据获取失败: {str(e)}"
            logger.error(error_msg)
            self.kline_status_label.setText(f"K线数据: 获取失败 ({str(e)})")
            return []

    def start_simulation(self, test_mode=False, test_data=None):
        """开始模拟（集成价格轮询与撮合）
        
        Args:
            test_mode: 是否启用测试模式
            test_data: 测试数据列表，仅在test_mode=True时有效
        """
        if self.server_running:
            error_msg = "服务器已经在运行中！"
            logger.error(error_msg)
            QMessageBox.critical(self, "错误", error_msg)
            return
            
        if not test_mode and not data_cache.get_data():
            QMessageBox.warning(
                self,
                "提示",
                "未检测到历史数据，请先点击'获取历史数据'按钮获取数据。"
            )
            return
            
        if not db_manager.connect():
            error_msg = "无法连接到数据库，请检查数据库文件是否正确！"
            logger.error(error_msg)
            QMessageBox.critical(self, "错误", error_msg)
            return
            
        # 添加WebSocketManager检查和初始化
        if not hasattr(self, 'ws_manager') or self.ws_manager is None:
            logger.error("WebSocketManager未初始化，使用全局实例")
            from websocket.ws_routes import ws_manager
            self.ws_manager = ws_manager
        
        # 确保 ws_manager 处于运行状态
        self.ws_manager.start_simulation()
        
        # 检查并记录WebSocketManager状态
        logger.info(f"WebSocketManager状态: 客户端连接数={len(self.ws_manager.active_connections)}, "
                    f"订单频道订阅数={len(self.ws_manager.order_subscribers)}, "
                    f"ticker频道订阅数={len(self.ws_manager.ticker_subscribers)}")
        
        logger.info(f"成功连接到数据库: {self.db_path_edit.text()}")
        
        # 根据模式选择数据
        if test_mode:
            if not test_data:
                logger.warning("测试模式但未提供测试数据，将使用默认测试数据")
                test_data = [1000.0 + i for i in range(10)]  # 示例测试数据
            prices = test_data
            logger.info(f"[测试模式] 使用 {len(prices)} 条测试数据")
        else:
            data_info = data_cache.get_data_info()
            logger.info(
                "\n=================== 开始模拟交易 ===================\n"
                f"K线粒度: {data_info['granularity']}\n"
                f"时间范围: {data_info['time_range']}\n"
                f"数据总量: {data_info['total_count']}\n"
                "=================================================="
            )
            # 刷新订单缓存，保证撮合时有最新订单
            from cache.order_cache import order_cache
            order_cache.refresh()
            prices = data_cache.get_data()
            logger.info(f"获取到 {len(prices)} 条价格数据，准备开始模拟...")
        
        # 创建价格匹配器（根据模式选择不同的实现）
        if test_mode:
            from service.test_price_matcher import TestPriceMatcher
            self.price_matcher = TestPriceMatcher(
                prices,
                self.push_callback,
                interval_ms=1000,
                ws_manager=self.ws_manager
            )
            self.price_matcher.enable_test_mode()
            if test_data:
                self.price_matcher.inject_test_data(test_data)
        else:
            from service.price_matcher import PriceMatcher
            self.price_matcher = PriceMatcher(
                prices,
                self.push_callback,
                interval_ms=1000,
                ws_manager=self.ws_manager
            )
        
        self.price_matcher.start()
        self.matching_status_label.setText("撮合轮询: 运行中")
        
        # 启动服务器线程
        if not self.server_thread or not self.server_thread.is_alive():
            self.server_thread = threading.Thread(target=self.run_server)
            self.server_thread.daemon = True
            self.server_thread.start()
            logger.info("服务器线程已启动")
            
        self.server_running = True
        self.start_button.setEnabled(False)
        self.stop_button.setEnabled(True)
        self.server_status_label.setText("服务器状态: 运行中")
        self.update_db_status()

    def start_async_fetch(self):
        """启动异步数据获取"""
        def run_async():
            """在新线程中运行异步操作"""
            if not self.data_fetcher:
                logger.error("数据获取器未初始化")
                return
                
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                loop.run_until_complete(self.fetch_kline_data())
            except Exception as e:
                logger.error(f"数据获取失败: {str(e)}")
                self.kline_status_label.setText(f"K线数据: 获取失败 ({str(e)})")
            finally:
                try:
                    # 确保所有待处理的任务都被取消
                    pending = asyncio.all_tasks(loop)
                    for task in pending:
                        task.cancel()
                    # 运行一次以处理取消的任务
                    if pending:
                        loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
                    loop.close()
                except Exception as e:
                    logger.error(f"关闭事件循环时出错: {str(e)}")
                finally:
                    # 恢复按钮状态
                    self.fetch_history_button.setEnabled(True)
                    self.fetch_history_button.setText("获取历史数据")

        # 在新线程中运行异步操作
        thread = threading.Thread(target=run_async)
        thread.daemon = True
        thread.start()

    def stop_simulation(self):
        """停止模拟（停止价格轮询与撮合）"""
        if self.server_running:
            logger.info("停止模拟服务")
            self.server_running = False
            self.start_button.setEnabled(True)
            self.stop_button.setEnabled(False)
            self.server_status_label.setText("服务器状态: 已停止")
            db_manager.close()
            logger.info("数据库连接已关闭")
            self.update_db_status()
            if self.current_port:
                try:
                    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                        s.connect(('localhost', self.current_port))
                        s.close()
                except:
                    pass
                self.current_port = None
            self.data_fetcher = None
            # 注意：不再调用data_cache.clear()，保留数据以便K线图使用
            # 停止价格轮询与撮合
            if self.price_matcher:
                self.price_matcher.stop()
                self.price_matcher = None
            self.matching_status_label.setText("撮合轮询: 已停止")

    def update_status(self):
        client_count = len(self.ws_manager.active_connections)
        self.ws_client_count_label.setText(f'已连接客户端: {client_count}')
        if client_count != getattr(self, '_last_client_count', None):
            logger.info(f'当前连接的客户端数量: {client_count}')
            self._last_client_count = client_count
        
    def run_server(self, port=None):
        """运行服务器，支持指定端口"""
        if self.server_running:
            logger.warning("服务器已经在运行中")
            return
        if port is None:
            port = find_available_port(PORT_CONFIG["DEFAULT_WS_PORT"])
        else:
            port = find_available_port(port)
        if port != PORT_CONFIG["DEFAULT_WS_PORT"]:
            logger.warning(f"端口{PORT_CONFIG['DEFAULT_WS_PORT']}被占用，使用新端口: {port}")
        self.current_port = port
        logger.info(f"启动WebSocket服务器 - 端口: {port}")
        try:
            uvicorn.run("main:app", host="0.0.0.0", port=port, reload=False)
        except Exception as e:
            logger.error(f"服务器启动失败: {str(e)}")
            self.server_running = False
            self.start_button.setEnabled(True)
            self.stop_button.setEnabled(False)
            self.server_status_label.setText("服务器状态: 启动失败")
            QMessageBox.critical(self, "错误", f"服务器启动失败: {str(e)}")
            self.current_port = None

    def closeEvent(self, event):
        logger.info("关闭应用程序")
        self.ws_manager.stop_simulation()
        # 确保事件循环被清理
        if self.event_loop and not self.event_loop.is_closed():
            self.event_loop.close()
        self.ws_client_update_timer.stop()  # 停止定时器
        if self.server_thread and self.server_thread.is_alive():
            self.stop_websocket_server()
        event.accept()

    def push_callback(self, order, price, order_type):
        """当订单命中价格时被调用"""
        msg = f"推送: {order_type} 订单 {order['order_id']} 命中价格 {price}"
        logger.info(msg)
        
        # 检查订阅者数量并记录
        subscriber_count = len(self.ws_manager.order_subscribers)
        logger.info(f"当前订单频道订阅者数量: {subscriber_count}")
        if subscriber_count > 0:
            subscriber_ids = ", ".join(str(client_id) for client_id in self.ws_manager.order_subscribers.keys())
            logger.info(f"订阅者客户端ID列表: {subscriber_ids}")
        else:
            logger.warning("没有客户端订阅订单频道，消息将无法推送")
            
        # 异步推送到WebSocket客户端
        async def push_to_ws():
            try:
                logger.info(f"开始推送订单到WebSocket客户端, 订单ID: {order['order_id']}, 价格: {price}, 类型: {order_type}")
                await self.ws_manager.push_order_update(order_type, order, price)
                logger.info(f"完成推送订单到WebSocket客户端, 订单ID: {order['order_id']}")
            except Exception as e:
                logger.error(f"推送订单更新到WebSocket客户端失败: {str(e)}")
        
        # 创建事件循环并运行异步任务
        try:
            logger.info(f"准备创建异步任务推送订单, 订单ID: {order['order_id']}")
            loop = asyncio.get_event_loop()
            
            # 检查事件循环是否已关闭
            if loop.is_closed():
                logger.warning(f"事件循环已关闭，创建新的事件循环, 订单ID: {order['order_id']}")
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
        except RuntimeError:
            # 如果没有事件循环，创建一个新的
            logger.warning(f"未找到事件循环，创建新的事件循环, 订单ID: {order['order_id']}")
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
        
        # 运行异步任务
        try:
            loop.run_until_complete(push_to_ws())
        except Exception as e:
            logger.error(f"执行WebSocket推送时发生错误: {str(e)}")

    def test_db_connection(self):
        """测试数据库连接"""
        # Ensure db_manager uses the current path for testing
        db_manager.update_db_path(self.db_path_edit.text())
        import sqlite3
        db_path = self.db_path_edit.text()
        try:
            conn = sqlite3.connect(db_path)
            conn.execute('SELECT 1')
            conn.close()
            QMessageBox.information(self, "数据库连接测试", f"成功连接到数据库: {db_path}")
        except Exception as e:
            QMessageBox.critical(self, "数据库连接测试", f"连接失败: {e}")

    def connect_db(self):
        """连接数据库"""
        db_manager.update_db_path(self.db_path_edit.text()) # Ensure path is up-to-date
        if db_manager.connect():
            QMessageBox.information(self, "数据库连接", f"成功连接到数据库: {db_manager.DB_PATH}")
        else:
            QMessageBox.critical(self, "数据库连接", "数据库连接失败，请检查路径或文件权限！")
        self.update_db_status() # Update label and button states

    def disconnect_db(self):
        """断开数据库连接"""
        db_manager.close()
        QMessageBox.information(self, "数据库连接", "数据库连接已断开")
        self.update_db_status()

    def start_websocket_server(self):
        """启动WebSocket服务"""
        if hasattr(self, 'ws_server_thread') and self.ws_server_thread and self.ws_server_thread.is_alive():
            QMessageBox.information(self, "提示", "WebSocket服务已在运行中！")
            return
        port = self.port_spinbox.value()
        self.ws_server_thread = threading.Thread(target=self.run_server, args=(port,))
        self.ws_server_thread.daemon = True
        self.ws_server_thread.start()
        self.start_ws_btn.setEnabled(False)
        self.stop_ws_btn.setEnabled(True)
        QMessageBox.information(self, "提示", f"WebSocket服务已启动，端口: {port}")

    def stop_websocket_server(self):
        """停止WebSocket服务"""
        import requests
        # 这里只能通过暴力方式终止 uvicorn 进程（如用 os._exit 或其它方式），这里简单演示按钮状态切换
        # 实际生产建议用更优雅的方式管理进程
        if hasattr(self, 'ws_server_thread') and self.ws_server_thread:
            # 这里仅做按钮状态切换，实际可用信号/标志位/进程管理等
            self.start_ws_btn.setEnabled(True)
            self.stop_ws_btn.setEnabled(False)
            QMessageBox.information(self, "提示", "WebSocket服务已停止（请手动关闭后台进程）")

    def update_ws_client_count(self):
        """更新WebSocket客户端连接数量显示"""
        if self.ws_manager:
            count = len(self.ws_manager.active_connections)
            self.ws_client_count_label.setText(f"WebSocket连接数: {count}")
            if count != getattr(self, '_last_client_count', None):
                logger.info(f'当前连接的客户端数量: {count}')
                self._last_client_count = count

    def update_time_span(self):
        """更新时间跨度显示"""
        start_dt = self.start_time.dateTime().toPyDateTime()
        end_dt = self.end_time.dateTime().toPyDateTime()
        time_diff = end_dt - start_dt
        days = time_diff.days
        hours = time_diff.seconds // 3600
        minutes = (time_diff.seconds % 3600) // 60
        
        span_text = "时间跨度: "
        if days > 0:
            span_text += f"{days}天 "
        if hours > 0:
            span_text += f"{hours}小时 "
        if minutes > 0:
            span_text += f"{minutes}分钟"
            
        self.time_span_label.setText(span_text)

    def fetch_history_data(self):
        """获取历史数据"""
        if not self.validate_time_range():
            logger.warning("时间范围验证失败")
            return

        # 创建新的数据获取器
        self.data_fetcher = KlineDataFetcher()
        
        # 使用QTimer来执行异步操作
        QTimer.singleShot(0, self.start_async_fetch)
        
        # 更新按钮状态
        self.fetch_history_button.setEnabled(False)
        self.fetch_history_button.setText("正在获取数据...")

    def show_custom_data_dialog(self):
        """显示自定义数据对话框"""
        dialog = CustomDataDialog(self)
        if dialog.exec() == QDialog.DialogCode.Accepted:
            custom_data = dialog.get_custom_data()
            if not custom_data:
                QMessageBox.warning(self, "警告", "请输入有效的数字数据！")
                return
            
            start_dt = datetime.now()
            end_dt = datetime.now() + timedelta(minutes=len(custom_data))
            granularity = "custom"
            
            # 更新数据缓存 (用于现有逻辑)
            data_cache.update_data(custom_data, start_dt, end_dt, granularity, "自定义数据")
            
            # 为OHLC图表创建模拟数据
            from utils.ohlc_data_manager import ohlc_data_manager
            
            # 创建模拟OHLC数据 (每个价格点转为一个K线)
            raw_ohlc_data = []
            
            # 以当前时间为基准，每分钟一个K线
            base_time = int(start_dt.timestamp() * 1000)
            interval = 60 * 1000  # 1分钟 (毫秒)
            
            for i, price in enumerate(custom_data):
                # 创建模拟K线数据 [timestamp, open, high, low, close, volume]
                # 使用相同的价格作为OHLC值，这样K线就是一条横线
                timestamp = base_time + i * interval
                raw_ohlc_data.append([
                    str(timestamp),
                    str(price),
                    str(price * 1.001),  # 稍高一点作为最高价
                    str(price * 0.999),  # 稍低一点作为最低价
                    str(price),
                    "100"  # 模拟成交量
                ])
            
            # 存储到OHLC数据管理器
            ohlc_count = ohlc_data_manager.store_ohlc_data(raw_ohlc_data, granularity, start_dt, end_dt)
            logger.info(f"已为自定义数据创建 {ohlc_count} 条模拟OHLC数据用于图表显示")
            
            self.kline_status_label.setText("K线数据: 已设置 " + str(len(custom_data)) + " 条自定义数据")
            msg = "已成功设置 " + str(len(custom_data)) + " 条自定义数据\n您现在可以点击\"开始模拟\"按钮开始测试了。"
            QMessageBox.information(self, "成功", msg)

    def show_kline_chart(self):
        """显示K线图表"""
        from utils.ohlc_data_manager import ohlc_data_manager
        
        # 检查是否有数据
        if ohlc_data_manager.get_data_info()["total_count"] == 0:
            QMessageBox.warning(
                self,
                "提示",
                "未检测到K线数据，请先获取历史数据。"
            )
            return
        
        # 导入并显示图表
        from ui.chart_viewer import ChartViewerDialog
        chart_dialog = ChartViewerDialog(self)
        chart_dialog.exec()

def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

if __name__ == "__main__":
    main() 